package de.adson.navigation;
import java.text.NumberFormat;
import java.util.Locale;

public class Parser {

	
	String nmea_string, nmea_type;
	public double sog = -1;
	public double cog = -1;
	public double aws, awa, tws, twa, twd;
	public double tSpeed, perf;
	public boolean rmc = false;
	public boolean mwvR = false;
	public boolean mwvRCalc = false;
	public boolean mwvRin = false;
	public boolean mwvT = false;
	public boolean mwvTCalc = false;
	public boolean mwvTin = false;
	public int rmcAge, mwvRAge, mwvTAge;
	Polaren polar = new Polaren("Polaren.txt");

	public Parser() {
		super();
		
	}

	public void parse(String string) {
		nmea_string = string;
		double a, b;
		double y, bb;

		// check nmea checksum
		if (check(nmea_string)) {
			// check that it is really a begin of a standard NMEA sentence
			if (nmea_string.substring(0, 1).equals("$")) {
				String nmea_array[] = nmea_string.split(",");

				// this might be necessary if data has spaces or s.o.else
				// for (int i=0; i<nmea_array.length; i++)
				// nmea_array[i].trim();

				// check for specific sentences if there is s.th. to do)
				nmea_type = nmea_array[0].substring(3, 6);
				if (nmea_type.equals("RMC")) {
					sog = Double.valueOf(nmea_array[7]).floatValue();
					cog = Double.valueOf(nmea_array[8]).floatValue();
					rmc = true;
					rmcAge = 0;
					System.out.println("PARSER:    SOG=" + sog + " COG=" + cog);
				} else if (nmea_type.equals("MWV")) {

					if (nmea_array[2].equals("R")) {
						aws = Double.valueOf(nmea_array[3]).floatValue();
						awa = Double.valueOf(nmea_array[1]).floatValue();
						mwvR = true;
						mwvRAge = 0;
						mwvRin = true;
						System.out.println("PARSER:    AWS=" + aws + " AWA="+ awa);
						if (rmc &&!mwvTin) {
							y = 90 - awa;
							a = aws * Math.cos(Math.toRadians(y));
							bb = aws * Math.sin(Math.toRadians(y));
							b = bb - sog;
							tws = Math.sqrt(a * a + b * b);
							if (Math.abs(a) < 0.01) {
								if ((sog <= aws)
										&& ((Math.abs(awa) < 1) || (Math
												.abs(awa) > 359))) {
									twa = 0;
								} else {
									twa = 180;
								}
							}

							else {
								twa = 90 - Math.toDegrees(Math.atan(b / a));
								if (awa > 180)
									twa = twa + 180;
							}
							twd = twa + cog;
							if (twd > 360)
								twd = twd - 360;
							mwvT = true;
							mwvTCalc = true;
							mwvTAge = 0;
							System.out.println("PARSER:    AWS=" + aws + " AWA="+ awa+ " SOG=" + sog+ " COG=" + cog + " ==> TWS=" + tws + " TWA=" + twa + " TWD=" + twd);
						}
					}

					else if (nmea_array[2].equals("T")) {
						tws = Double.valueOf(nmea_array[3]).floatValue();
						twa = Double.valueOf(nmea_array[1]).floatValue();
						twd = twa + cog;
						if (twd > 360)
							twd = twd - 360;
						mwvT = true;
						mwvTAge = 0;
						mwvTin = true;
						System.out.println("PARSER:    TWS=" + tws + " twa="+ twa + " twd=" + twd);
						if (rmc && !mwvRin) {
							y = 90 - twa;
							a = tws * Math.cos(Math.toRadians(y));
							b = tws * Math.sin(Math.toRadians(y));
							bb = b + sog;
							aws = Math.sqrt(a * a + bb * bb);
							if (Math.abs(a) < 0.01) {
								if ((sog >= tws)
										&& (Math.abs((Math.abs(twa) - 180)) < 1)) {
									awa = 0;
								} else {
									awa = 180;
								}
							} else {
								awa = 90 - Math.toDegrees(Math.atan(bb / a));
								if (twa > 180)
									awa = awa + 180;
							}
							mwvR = true;
							mwvRCalc = true;
							mwvRAge = 0;
							System.out.println("PARSER:    TWS=" + tws + " TWA="+ twa + " SOG=" + sog
									+ " COG=" + cog + " ==> AWS=" + aws
									+ " AWA=" + awa);
						}
					}
				}
			}
		}
	}

	public void age (){
		int alt = SailTac.DATEN_ALT;
		rmcAge++;mwvTAge++;mwvRAge++;
		if (rmcAge > alt) rmc = false;
		if (mwvTAge > alt) {mwvT = false;mwvTin=false;}
		if (mwvRAge > alt) {mwvR = false;mwvRin=false;}
		System.out.println("DEBUG:     Ageing Data "+rmcAge+" "+mwvRAge +" "+ mwvTAge);
	}

		public void generateNMEA(WriteSerialNmea nmeaOut,DbCon con) {
		NumberFormat grad = NumberFormat.getInstance(Locale.UK);
		grad.setMaximumFractionDigits(1);
		grad.setMinimumIntegerDigits(3);
		
		if (!mwvTin && mwvTCalc) {
			String nmeaMWV = "MWV," + grad.format(twa) + ",T,"+ grad.format(tws) + ",N,A";
			sendNmea(nmeaMWV, nmeaOut,con);
			mwvTCalc = false;
		}
		if (!mwvRin && mwvRCalc) {
			String nmeaMWV = "MWV," + grad.format(awa) + ",R,"+ grad.format(aws) + ",N,A";
			sendNmea(nmeaMWV, nmeaOut,con);
			mwvRCalc = false;
		}
		if (mwvT){
			tSpeed = polar.getTargetSpeed(tws,twa);
			perf = sog/tSpeed;
			System.out.println("DEBUG:     TARGETSPEED="+grad.format(tSpeed)+" PERFORMANCE="+grad.format(perf*100));
			String nmeaXXX = "XXX," + grad.format(tSpeed) + ",,"+ grad.format(perf*100) + ",N,A";
			sendNmea(nmeaXXX, nmeaOut,con);
		}

	}
	public void sendNmea (String nmeaString,WriteSerialNmea nmeaOut,DbCon con){ 
		nmeaString= formatNmea(nmeaString); //add Checksum, Header and cr/lf
		nmeaOut.write(nmeaString); // output to serial
		if (SailTac.DB) con.insertNmeaThreaded(nmeaString); // and to db
		}

	public String formatNmea(String s) {
		String nmea_string;
		nmea_string = "$AE" + s;
		nmea_string = nmea_string + "*" + this.GetCheckSum(nmea_string)+"\r";
		return (nmea_string);
	}

	/**
	 * calculates checksum of NMEA message and compares it
	 * 
	 * 
	 * @param msg
	 * @return true if the checksum is valid, false if it is invalid
	 */
	private boolean check(String msg) {

		boolean IAmValid = false;

		// calculate message length and subtract one (the carriage return)
		int msglen = msg.length() - 1;

		// if the message length is greater than 4 characters
		if (msglen > 4) {
			// check if there is a checksum in this message by seeing where the
			// asterisk is
			// (it should be at the third last position in the message)
			if (msg.charAt(msglen - 3) == '*') {
				// System.out.println("    received checksum : " +
				// msg.substring(msglen - 2, msglen));
				// perform NMEA checksum calculation
				String chk_s = GetCheckSum(msg.substring(0, msglen - 3));

				// compare checksum to encoded checksum in msg
				IAmValid = msg.substring(msglen - 2, msglen).equals(chk_s);
				if (!IAmValid)
					System.out
					.println("PARSER:    Checksum: message checksum is wrong (should be "
							+ chk_s + " )");
				return (IAmValid);
			} else {
				// message doesn't have a checksum: do not accept it
				System.out
				.println("PARSER:    Checksum: message has no checksum.");
				return false;
			}
		}
		// (if the message length is greater than 4 characters)
		// don't accept messages without checksum
		return false;
	}

	/**
	 * Trims the checksum off an NMEA message, then recalculates the checksum to
	 * compare it with the one passed along with the message later
	 * 
	 * @param msg
	 *            String containing the full NMEA message (including checksum)
	 * @return String containing the checksum
	 */
	public static String GetCheckSum(String msg) {
		// perform NMEA checksum calculation
		int chk = 0;
		// run through each character of the message length
		// and XOR the value of chk with the byte value
		// of the character that is being evaluated
		for (int i = 1; i < msg.length(); i++) {
			chk ^= msg.charAt(i);
		}

		// convert the retreived integer to a HexString in uppercase
		String chk_s = Integer.toHexString(chk).toUpperCase();

		// checksum must be 2 characters!
		// if it falls short, add a zero before the checksum
		while (chk_s.length() < 2) {
			chk_s = "0" + chk_s;
		}

		// show the calculated checksum
		// System.out.println("    calculated checksum : " + chk_s);

		// return the calculated checksum
		return chk_s;
	}
}
